package com.gitee.l0km.aocache.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.SuppressAjWarnings;

import com.gitee.l0km.aocache.Timecost;

import static com.gitee.l0km.aocache.MemberCache.INSTANCE;

/**
 * 
 * 基于{@link com.gitee.l0km.aocache.annotations.AoCacheable}注解的方法缓存切面定义<br>
 * @author guyadong
 *
 */
@Aspect
public class AocacheAnnotatedAspect {
	@SuppressAjWarnings("adviceDidNotMatch")
	@Around("aoCacheable() && (range() || ctor())")
    public Object cacheRround(ProceedingJoinPoint joinPoint) throws Throwable {
		return INSTANCE.around(joinPoint);
    }

    /**
     * 定义切入点为有{@link com.gitee.l0km.aocache.annotations.AoCacheable}注解的所有函数
     */
    @Pointcut("@annotation(com.gitee.l0km.aocache.annotations.AoCacheable)||@annotation(com.gitee.l0km.aocache.annotations.AoWeakCacheable)")
    public void aoCacheable() {
    }

    /**
     * 定义切入点为有{@link com.gitee.l0km.aocache.annotations.AoUpdateable}注解的所有函数
     */
    @Pointcut("@annotation(com.gitee.l0km.aocache.annotations.AoUpdateable)")
    public void aoUpdateable() {
    }
    /**
     * 定义切入点为所有返回类型不是void的方法执行
     */
    @Pointcut("execution(!void *.*(..))")
    public void range() {
    }
    /**
     * 定义切入点为任意构造方法执行(private除外)调用<br>
     * aspectJ不能拦截私有构造方法<br>
     * 参见 <a ref="https://stackoverflow.com/questions/63187952/aspectj-intercept-constructor-when-method-reference-is-used">《AspectJ: intercept constructor when method reference is used》</a>
     */
    @Pointcut("call(*.new(..))")
    public void ctor() {
    }
    
    /**
     * 定义切入点为任意方法执行
     */
    @Pointcut("execution(* *.*(..))")
    public void clearRange() {
    }
    /**
     * 定义切入点为有{@link com.gitee.l0km.aocache.annotations.AoClear}或{@link com.gitee.l0km.aocache.annotations.AoClears}注解
     */
    @Pointcut("@annotation(com.gitee.l0km.aocache.annotations.AoClear) || @annotation(com.gitee.l0km.aocache.annotations.AoClears)")
    public void aoClear() {
    }
    
	@SuppressAjWarnings("adviceDidNotMatch")
	@AfterReturning(pointcut = "aoClear() && clearRange()", returning = "result")
	public void clearAfterReturning(JoinPoint point, Object result) throws Throwable {
		INSTANCE.afterReturingClear(point, result);
	}

    /**
     * [执行]定义update切入点定义<br>
     * 切入点为包括(UpdateStrategy updateStrategy,Object target, Object[] args,Object value)参数的方法
     * <ul>
     * <li>第一个参数为{@link com.gitee.l0km.aocache.UpdateStrategy}类型,定义更新策略，为{@code null}忽略</li>
     * <li>第二个参数为{@link Object}数组类型，方法调用参数</li>
     * <li>第三个参数为任意类型，要更新的值,由调用者保证类型匹配</li>
     * <li>第四个参数为任意非primitive类型[可选],方法的执行目标，对静态方法为{@code null}</li>
     * <li>第五个参数为{@link Object}数组类型[可选项]，要删除的参数，由调用者保证类型匹配，如果为非primitive类型</li>
     * </ul>
     * NOTE:第四/第五个参数的位置可以互换,如果为Object[]即作为要删除的参数，否则作为方法的执行目标
     */
    @Pointcut("execution(* *.*(com.gitee.l0km.aocache.UpdateStrategy,Object[],*,..))")
    public void updateRange() {
    }
    /**
     * 处理@AoUpdateable注解的切面方法
     * 在方法执行前更新指定的缓存
     * 
     * @param joinPoint 切入点对象
     * @throws Throwable 如果更新过程中发生错误
     */
    @SuppressAjWarnings("adviceDidNotMatch")
    @Before("aoUpdateable() && updateRange()")
    public void beforeUpdate(JoinPoint joinPoint) throws Throwable {
        INSTANCE.beforeUpdate(joinPoint);
    }
    /**
     * 定义切入点为有{@link com.gitee.l0km.aocache.annotations.AoTimecost}注解
     */
    @Pointcut("@annotation(com.gitee.l0km.aocache.annotations.AoTimecost)")
    public void aoTimecost() {
    }
    /**
     * 定义切入点为所有方法执行
     */
    @Pointcut("execution(* *.*(..))")
    public void timecostRange() {
    }
    /** 输出方法/构造方法执行时间 */
    @SuppressAjWarnings("adviceDidNotMatch")
	@Around("aoTimecost() && (timecostRange() || ctor())")
    public Object timecostAround(ProceedingJoinPoint joinPoint) throws Throwable {
		return Timecost.around(joinPoint);
    }
    public static AocacheAnnotatedAspect aspectOf() {
    	return new AocacheAnnotatedAspect();
    }
}
